#include "CEUtilities.h"

#include "CETextureManager.h"
#include "CEMaterialLoader.h"
#include "CEGraphicLayer.h"

#include "CEMaterial.h"

Material Material::LastMaterial = Material();
Material* Material::GUIMaterial  = Material::GetGUIMaterial();


//----------------------------------------------------------------------
Material::Material()
{
	m_AmbientColor = Color::White;
	m_DiffuseColor = Color::White;
	m_SpecularColor = Color::White;
	m_fSpecularPower = 0.0f;
	m_EmissiveColor = Color::Black;

	m_dwAlphaReference = 0;
	m_dwAlphaBlendFactor = 0;

	m_AlphaTestFunc = D3DCMP_GREATEREQUAL;
	m_DepthWriteFunc = D3DCMP_LESSEQUAL;
	
	m_Culling = D3DCULL_CCW;
	m_TextureFilterMode = D3DTEXF_POINT;
	m_ShadeMode = D3DSHADE_GOURAUD;
	m_SceneBlend = SBT_ADD;
	
	m_bAlphaTestEnabled = FALSE;
	m_bIsTransparent = FALSE;
	m_bLighting = TRUE;
	m_bDepthTestEnabled = TRUE;
	m_bDepthWriteEnabled = TRUE;

	m_eShadowType = ST_DISABLE;
}


//----------------------------------------------------------------------
Material::~Material()
{
	for (size_t i=0; i<m_Textures.size(); ++i)
	{
		if  (m_Textures[i].TextureName != "")
		{
			TextureManager::GetSingleton()->RemoveResource(m_Textures[i].TextureName);
		}
	}

	m_Textures.clear();
}


//----------------------------------------------------------------------
Material* Material::GetGUIMaterial()
{
	if  (GUIMaterial)
	{
		GUIMaterial->SetLighting(FALSE);

		GUIMaterial->SetAlphaTest(TRUE);
		GUIMaterial->SetAlphaTestFunc(D3DCMP_GREATER);
		GUIMaterial->SetAlphaReference(0);

		GUIMaterial->AddTextureLayer();
		GUIMaterial->SetTextureColorOP(D3DTOP_MODULATE);
		GUIMaterial->SetTextureAlphaOP(D3DTOP_MODULATE);

		GUIMaterial->SetTransparent(TRUE);
		GUIMaterial->SetSceneBlendType(SBT_ALPHA_BLEND);
	}
	
	return GUIMaterial;
}


//----------------------------------------------------------------------
D3DMATERIAL9 Material::GetD3DMaterial()
{
	m_lpMaterial.Ambient = m_AmbientColor.GetAsD3DCOLOR();
	m_lpMaterial.Diffuse = m_DiffuseColor.GetAsD3DCOLOR();
	m_lpMaterial.Emissive = m_EmissiveColor.GetAsD3DCOLOR();
	m_lpMaterial.Specular = m_SpecularColor.GetAsD3DCOLOR();
	m_lpMaterial.Power = m_fSpecularPower;

	return m_lpMaterial;
}


//----------------------------------------------------------------------
void Material::AddTextureLayer()
{
	TextureLayer newTexture;

	newTexture.TextureName = "";
	newTexture.ColorOP = D3DTOP_ADD;
	newTexture.AlphaOP = D3DTOP_DISABLE;

	m_Textures.push_back(newTexture);
}


//----------------------------------------------------------------------
void Material::AddTexture(STRING name)
{
	TextureManager::GetSingleton()->Create2DTexture(name);
	m_Textures.back().TextureName = name;
}


//----------------------------------------------------------------------
void Material::SetTextureColorOP(D3DTEXTUREOP ColorOP)
{
	m_Textures.back().ColorOP = ColorOP;
}


//----------------------------------------------------------------------
void Material::SetTextureAlphaOP(D3DTEXTUREOP AlphaOP)
{
	m_Textures.back().AlphaOP = AlphaOP;
}


//----------------------------------------------------------------------
void Material::SetFromMaterial()
{
	static BOOL bFirstTime = TRUE;

	GraphicLayer *lpgraphiclayer = GraphicLayer::GetSingleton();

	lpgraphiclayer->GetD3DDevice()->SetMaterial(&(this->GetD3DMaterial()));
	
	if (bFirstTime || LastMaterial.GetDepthTest()!=m_bDepthTestEnabled)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ZENABLE,m_bDepthTestEnabled);
	}
	if (bFirstTime || LastMaterial.GetDepthWrite()!=m_bDepthWriteEnabled)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ZWRITEENABLE,m_bDepthWriteEnabled);
	}
	if (bFirstTime || LastMaterial.GetDepthWriteFunc()!=m_DepthWriteFunc)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ZFUNC,m_DepthWriteFunc);
	}

	if (bFirstTime || LastMaterial.GetAlphaTest()!=m_bAlphaTestEnabled)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ALPHATESTENABLE,m_bAlphaTestEnabled);
	}
	if (bFirstTime || LastMaterial.GetAlphaReference()!=m_dwAlphaReference)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ALPHAREF,m_dwAlphaReference);
	}
	if (bFirstTime || LastMaterial.GetAlphaTestFunc()!=m_AlphaTestFunc)
	{
		lpgraphiclayer->SetRenderState(D3DRS_ALPHAFUNC,m_AlphaTestFunc);
	}

	if (bFirstTime || LastMaterial.GetCullingMode()!=m_Culling)
	{
		lpgraphiclayer->SetRenderState(D3DRS_CULLMODE,m_Culling);
	}
	if (bFirstTime || LastMaterial.GetLighting()!=m_bLighting)
	{
		lpgraphiclayer->SetRenderState(D3DRS_LIGHTING,m_bLighting);
	}
	if (bFirstTime || LastMaterial.GetShadeMode()!=m_ShadeMode)
	{
		lpgraphiclayer->SetRenderState(D3DRS_SHADEMODE,m_ShadeMode);
	}

	vector<TextureLayer>::iterator i;
	int j=0;
	for (i = m_Textures.begin(); i != m_Textures.end(); ++i,++j)
	{
		lpgraphiclayer->GetD3DDevice()->SetSamplerState(j,D3DSAMP_MINFILTER,m_TextureFilterMode);
		lpgraphiclayer->GetD3DDevice()->SetSamplerState(j,D3DSAMP_MAGFILTER,m_TextureFilterMode);

		if (j == 0)
		{
			if (i->TextureName!="")
				lpgraphiclayer->SetTexture(j,i->TextureName);

			lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
			lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE);
			lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLOROP,i->ColorOP);
			lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
			lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
			lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAOP,i->AlphaOP);
			
		}

		else
		{
			if (i->TextureName!="")
			{
				lpgraphiclayer->SetTexture(j,i->TextureName);
			}

			lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLORARG1,D3DTA_TEXTURE);
			lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLORARG2,D3DTA_CURRENT);
			lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLOROP,i->ColorOP);
			lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
			lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAARG2,D3DTA_CURRENT);
			lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAOP,i->AlphaOP);
		}
	}

	if (bFirstTime || (m_Textures.empty() && !LastMaterial.m_Textures.empty()))
	{
		lpgraphiclayer->GetD3DDevice()->SetTexture(0, NULL);
	}

	if (m_bIsTransparent)
	{
		if  (bFirstTime || LastMaterial.GetTransparent()!=m_bIsTransparent)
		{
			lpgraphiclayer->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
		}

		if (m_SceneBlend==SBT_CUSTOM)
		{
			if (bFirstTime || LastMaterial.m_SrcBlend!=m_SrcBlend)
			{
				lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,m_SrcBlend);
			}
			if (bFirstTime || LastMaterial.m_SrcBlend!=m_DestBlend)
			{
				lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,m_DestBlend);
			}
		}
		else if (bFirstTime || LastMaterial.GetSceneBlendType()!=m_SceneBlend)
		{
			switch (m_SceneBlend)
			{
				case SBT_ADD:		
					lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
					lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
					break;

				case SBT_MODULATE:
					lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR); 
					lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);					
					break;

				case SBT_ALPHA_BLEND:
	//				if (LastMaterial.GetUseAlphaBlendFactor()!=m_bUseAlphaBlendFactor){
						if (m_bUseAlphaBlendFactor){
							lpgraphiclayer->SetRenderState(D3DRS_BLENDFACTOR,m_dwAlphaBlendFactor);
							lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_BLENDFACTOR);
							lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVBLENDFACTOR);
						}
						else{
							lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
							lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
						}
						break;
	//				}
			}
		}
	}
	else
	{
		if (bFirstTime || LastMaterial.GetTransparent()!=m_bIsTransparent)
		{
			lpgraphiclayer->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
		}
	}
	
	LastMaterial = *this;

	bFirstTime = FALSE;
}


//----------------------------------------------------------------------
void Material::Load(STRING filename)
{
	MaterialLoader loader;
	loader.Load(filename, this);
}


//----------------------------------------------------------------------
void Material::RestoreLastMaterial()
{
	GraphicLayer *lpgraphiclayer = GraphicLayer::GetSingleton();

	lpgraphiclayer->GetD3DDevice()->SetMaterial(&(LastMaterial.GetD3DMaterial()));

	lpgraphiclayer->SetRenderState(D3DRS_ZENABLE,LastMaterial.m_bDepthTestEnabled);
	lpgraphiclayer->SetRenderState(D3DRS_ZWRITEENABLE,LastMaterial.m_bDepthWriteEnabled);
	lpgraphiclayer->SetRenderState(D3DRS_ZFUNC,LastMaterial.m_DepthWriteFunc);


	lpgraphiclayer->SetRenderState(D3DRS_ALPHATESTENABLE,LastMaterial.LastMaterial.m_bAlphaTestEnabled);
	lpgraphiclayer->SetRenderState(D3DRS_ALPHAREF,LastMaterial.m_dwAlphaReference);
	lpgraphiclayer->SetRenderState(D3DRS_ALPHAFUNC,LastMaterial.m_AlphaTestFunc);

	lpgraphiclayer->SetRenderState(D3DRS_CULLMODE,LastMaterial.m_Culling);
	lpgraphiclayer->SetRenderState(D3DRS_LIGHTING,LastMaterial.m_bLighting);
	lpgraphiclayer->SetRenderState(D3DRS_SHADEMODE,LastMaterial.m_ShadeMode);

	vector<TextureLayer>::iterator i;
	int j=0;
	for(i = LastMaterial.m_Textures.begin(); i !=LastMaterial.m_Textures.end(); ++i,++j)
	{
		lpgraphiclayer->GetD3DDevice()->SetSamplerState(j,D3DSAMP_MINFILTER,LastMaterial.m_TextureFilterMode);
		lpgraphiclayer->GetD3DDevice()->SetSamplerState(j,D3DSAMP_MAGFILTER,LastMaterial.m_TextureFilterMode);


	if(j==0){
		if(i->TextureName!="")
			lpgraphiclayer->SetTexture(j,i->TextureName);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_COLOROP,i->ColorOP);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
		lpgraphiclayer->SetTextureStageState(0,D3DTSS_ALPHAOP,i->AlphaOP);

	}

	else{
		if(i->TextureName!="")
			lpgraphiclayer->SetTexture(j,i->TextureName);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLORARG1,D3DTA_TEXTURE);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLORARG2,D3DTA_CURRENT);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_COLOROP,i->ColorOP);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAARG2,D3DTA_CURRENT);
		lpgraphiclayer->SetTextureStageState(j,D3DTSS_ALPHAOP,i->AlphaOP);
	}
}

	if(LastMaterial.m_bIsTransparent){
		lpgraphiclayer->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
		switch(LastMaterial.m_SceneBlend){
				case SBT_ADD:		
					lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
					lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
					break;
				case SBT_MODULATE:
					lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR); 
					lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);					
					break;
				case SBT_ALPHA_BLEND:
					if(LastMaterial.m_bUseAlphaBlendFactor){
						lpgraphiclayer->SetRenderState(D3DRS_BLENDFACTOR,LastMaterial.m_dwAlphaBlendFactor);
						lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_BLENDFACTOR);
						lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVBLENDFACTOR);
					}
					else{
						lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
						lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
					}
					break;
				case SBT_CUSTOM:
					lpgraphiclayer->SetRenderState(D3DRS_SRCBLEND,LastMaterial.m_SrcBlend);
					lpgraphiclayer->SetRenderState(D3DRS_DESTBLEND,LastMaterial.m_DestBlend);
			}
	}
	else
	{
		lpgraphiclayer->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
	}
}